#include "MediaSession.h"
#include "MediaSource.h"
#include "RTPConnection.h"
#include <ctime>
#include "NetInterface.h"
#include <forward_list>

namespace jlh
{
	std::atomic_uint MediaSession::last_session_id_(1);

	MediaSession* MediaSession::CreateNew(std::string url_suffix)
	{
		return new MediaSession(std::move(url_suffix));
	}

	MediaSession::MediaSession(const std::string& url_suffix)
		: suffix_(url_suffix)
		, media_sources_(2)

	{
		session_id_ = ++last_session_id_;
		for (int i = 0; i < MAX_MEDIA_CHANNEL; ++i)
		{
			multicast_port_[i] = 0;
		}
	}

	MediaSession::~MediaSession()
	{

	}

	bool MediaSession::AddSource(MediaChannelId channel_id, MediaSource* source)
	{
		source->SetSendFrameCallback([this](MediaChannelId channelId, RtpPacket pkt)->bool {
			std::forward_list<std::shared_ptr<RTPConnection>> clients;
			std::map<int, RtpPacket> packets;
			{
				std::lock_guard<std::mutex> l(map_mutex_);
				for (auto itr = clients_.begin(); clients_.end() != itr; )
				{
					auto conn = itr->second.lock();
					if (!conn)
						clients_.erase(itr++);
					else
					{
						int id = conn->GetId();
						if (0 <= id)
						{
							if (packets.end() == packets.find(id))
							{
								RtpPacket tmpPkt;
								memcpy(tmpPkt.data.get(), pkt.data.get(), pkt.size);
								tmpPkt.size = pkt.size;
								tmpPkt.last = pkt.last;
								tmpPkt.timestamp = pkt.timestamp;
								tmpPkt.type = pkt.type;
								packets.emplace(id, tmpPkt);
							}

							clients.emplace_front(conn);
						}
						++itr;
					}
				}
			}

			int count = 0;
			for (auto itr : clients)
			{
				int ret = 0;
				int id = itr->GetId();
				if (0 <= id)
				{
					auto itr2 = packets.find(id);
					if (packets.end() != itr2)
					{
						count++;
						ret = itr->SendRtpPacket(channelId, itr2->second);
						if (is_multicast_ && 0 == ret)
							break;
					}
				}
			}
		
			return true;
		});

		media_sources_[channel_id].reset(source);
		return true;
	}
	bool MediaSession::RemoveSource(MediaChannelId channel_id)
	{
		media_sources_[channel_id].reset();
		return true;
	}

	MediaSource* MediaSession::GetMediaSource(MediaChannelId channelId)
	{
		if (media_sources_[channelId]) {
			return media_sources_[channelId].get();
		}

		return nullptr;
	}

	bool MediaSession::AddClient(uint32_t rtspfd, std::shared_ptr<RTPConnection> rtp_conn)
	{
		std::lock_guard<std::mutex> l(map_mutex_);
		auto iter = clients_.find(rtspfd);
		if (clients_.end() == iter)
		{
			std::weak_ptr<RTPConnection> rtp_conn_weak_ptr = rtp_conn;
			clients_.emplace(rtspfd, rtp_conn_weak_ptr);
			if (notify_callback_)
				notify_callback_(session_id_, (uint32_t)clients_.size());

			has_new_client_ = true;
			return true;
		}
		return false;
	}

	void MediaSession::RemoveClient(uint32_t rtspfd)
	{
		std::lock_guard<std::mutex> l(map_mutex_);
		if (clients_.find(rtspfd) != clients_.end())
		{
			clients_.erase(rtspfd);
			if (notify_callback_)
				notify_callback_(session_id_, (uint32_t)clients_.size());
		}
	}

	std::string MediaSession::GetSdpMessage(std::string sessionName)
	{
		if (sdp_ != "")
			return sdp_;

		if (media_sources_.empty())
		{
			return "";
		}

		std::string ip = NetInterface::GetLocalIPAddress();
		char buf[2048] = { 0 };

		sprintf_s(buf, sizeof(buf),
			"v=0\r\n"
			"o=- 9%ld 1 IN IP4 %s\r\n"
			"t=0 0\r\n"
			"a=control:*\r\n",
			(long)std::time(NULL), ip.c_str());

		if ("" != sessionName)
		{
			sprintf_s(buf + strlen(buf), sizeof(buf) - strlen(buf),
				"s=%s\r\n",
				sessionName.c_str());
		}

		if (is_multicast_) {
			sprintf_s(buf + strlen(buf), sizeof(buf) - strlen(buf),
				"a=type:broadcast\r\n"
				"a=rtcp-unicast: reflection\r\n");
		}

		for (uint32_t chn = 0; chn < media_sources_.size(); ++chn)
		{
			if (media_sources_[chn])
			{
				if (is_multicast_) {

				}
				else {
					sprintf_s(buf + strlen(buf), sizeof(buf) - strlen(buf),
						"%s\r\n",
						media_sources_[chn]->GetMediaDescription(0).c_str());
				}

				sprintf_s(buf + strlen(buf), sizeof(buf) - strlen(buf),
					"%s\r\n",
					media_sources_[chn]->GetAttribute().c_str());
				sprintf_s(buf + strlen(buf), sizeof(buf) - strlen(buf),
					"a=control:track%d\r\n", chn);
			}
		}
		sdp_ = std::string(buf);
		return sdp_;
	}

	bool MediaSession::HandleFrame(MediaChannelId channel_id, AVFrame frame)
	{
		std::lock_guard<std::mutex> l(mutex_);
		if (media_sources_[channel_id])
		{
			media_sources_[channel_id]->HandleFrame(channel_id, frame);
			return true;
		}

		return false;
	}

}
